<template>
  <g class="text-relation" :data-relation-id="relation.id" :style="{ fontSize: fontSize + 'px' }">
    <path
      :d="d"
      :stroke="stroke"
      class="marker-end marker-start"
      :marker-end="'url(#triangle-end)'"
      :marker-start=" 'url(#triangle-start)'"
    />
    <!-- <text
      @dblclick="handleDblclick"
      @mouseenter="handleMouseenter"
      @mouseleave="handleMouseleave">
      <tspan
        onselectstart="return false"
        v-tooltip="tooltip"
        ref="tspan"
        :class="!initPosition && 'opacity'"
        :x="tspanX"
        :y="yline + fontSize * 0.4"
        :fill="fgColor">{{ relation.text }}</tspan>
    </text>-->
  </g>
</template>

<script>
export default {
  props: {
    disabled: Boolean,
    stroke: String,
    fontSize: Number,
    fgColor: String,
    rowStart: Number,
    rowEnd: Number,
    entityTypesMap: Object,
    // 如果 position 为 complete 则 relation.line 为顶线位置
    relation: Object
  },

  inject: ['handleEditRelation'],

  data () {
    return {
      // 文字放不下时的偏移量
      offset: 0,
      offsetRight: 0,
      // 定义三次贝塞尔曲线点间距
      round: 8,
      breakX1: undefined,
      breakX2: undefined,
      tspanX: 0,
      initPosition: false
    }
  },

  computed: {
    tooltip () {
      const { fromEntity, toEntity, id, text } = this.relation
      return {
        content: `
        <div class="marking-tooltip">
          <div>
            <span>${this.entityTypesMap[fromEntity[1]].name} → ${text} → ${this.entityTypesMap[toEntity[1]].name}</span>
            <span>ID: ${id}</span>
          </div>
          <div>"${fromEntity[3]}" → "${toEntity[3]}"</div>
        </div>
        `,
        html: true
      }
    },
    showMarkerEnd () {
      return this.relation.position === 'center' || this.relation.markerEnd
    },
    showMarkerStart () {
      return this.relation.position === 'center' || !this.relation.markerEnd
    },
    yline () {
      return this.relation.line || this.relation.y2
    },
    height () {
      return this.relation.y1 - this.yline
    },
    scale () {
      const { round, offset, height } = this
      const hypotenuse = +Math.sqrt((round + offset) * (round + offset) + height * height).toFixed(3)
      return round / hypotenuse
    },
    d () {
      const { yline, scale, height, round, offset, offsetRight } = this
      const { position, mode, x1, x2, y1, y2 } = this.relation
        
      let d = ''
      if (position === 'left' || position === 'complete') {
        d += `M${x1} ${y1 + 26}`

        // mode 为 in 时不考虑 offset
        if (mode === 'in') {
          const cx1 = x1 + (1 - scale) * round
          const cy1 = y1 - (1 - scale) * height
          d += ` L${cx1} ${cy1 + 26}`
          d += ` C${cx1} ${cy1 + 26} ${x1 + round} ${yline + 26} ${x1 + 2 * round} ${yline + 26}`

          // 给文字预留隔离位置
          d = this.addBreak(d)
        
          if (position === 'left') {
            d += ` L${x2} ${yline + 26}`
          }
          if (position === 'complete') {
            const cx1 = x2 - round + scale * round
            const cy1 = yline + scale * height
            d += ` L${x2 - 2 * round} ${yline + 26}`
            d += ` C${x2 - 2 * round} ${yline + 26} ${x2 - round} ${yline + 26} ${cx1} ${cy1 + 26}`
            d += ` L${x2} ${y2 + 26}`
          }
        }

        // mode 为 out 时需要考虑 offset
        if (mode === 'out') {
          const offsetX = round + offset
          const cx1 = x1 - (1 - scale) * offsetX
          const cy1 = y1 - (1 - scale) * height
          d += ` L${cx1} ${cy1 + 26}`
          d += ` C${cx1} ${cy1 + 26} ${x1 - offsetX} ${yline + 26} ${x1 - offset} ${yline + 26}`

          d = this.addBreak(d)

          if (position === 'left') {
            d += ` L${x2} ${yline + 26}`
          }
          if (position === 'complete') {
            const offsetXRight = round + offsetRight
            const cx1 = x2 + (1 - scale) * offsetXRight
            const cy1 = yline + scale * height
            d += ` L${x2 + offsetRight} ${yline + 26}`
            d += ` C${x2 + offsetRight} ${yline + 26} ${x2 + offsetXRight} ${yline + 26} ${cx1} ${cy1 + 26}`
            d += ` L${x2} ${y2}`
          }
        }

      } else if (position === 'right') {
        d += `M${x1} ${yline + 26}`
        
        d = this.addBreak(d)

        if (mode === 'in') {
          const cx1 = x2 - round + scale * round
          const cy1 = yline + scale * height
          d += ` L${x2 - 2 * round} ${yline + 26}`
          d += ` C${x2 - 2 * round} ${yline + 26} ${x2 - round} ${yline + 26} ${cx1} ${cy1 + 26}`
          d += ` L${x2} ${y1 + 26}`
        }

        if (mode === 'out') {
          const offsetX = round + offset
          const cx1 = x2 + (1 - scale) * offsetX
          const cy1 = yline + scale * height
          d += ` L${x2 + offset} ${yline + 26}`
          d += ` C${x2 + offset} ${yline + 26} ${x2 + offsetX} ${yline + 26} ${cx1} ${cy1 + 26}`
          d += ` L${x2} ${y1 + 26}`
        }
      } else if (position === 'center') {
        d += `M${x1} ${yline + 26}`

        d = this.addBreak(d)

        d += ` L${x2} ${yline + 26}`
      }

      return d
    },
    nodeClass () {
      const { id, fromEntity, toEntity } = this.relation
      return `g[data-relation-id="${id}"],g[data-label-id="${fromEntity[0]}"],g[data-label-id="${toEntity[0]}"]`
    }
  },

  methods: {
    addBreak (d) {
      const { yline, breakX1, breakX2 } = this
      if (typeof breakX1 === 'number' && typeof breakX2 === 'number') {
        d += ` L${breakX1} ${yline}`
        d += ` M${breakX2} ${yline}`
      }
      return d
    },
    getPosition () {
      const { rowStart, rowEnd } = this
      const { x1, x2, position } = this.relation
      const gap = x2 - x1
      if(!this.$refs.tspan) return
      const width = this.$refs?.tspan?.getBoundingClientRect()?.width || 0
      // 箭头长度
      const arrowWidth = 9
      // 文字左右各 3px 间隙
      const space = 0
      if (position === 'center') {
        if (gap > width + 2 * (space + arrowWidth)) {
          this.tspanX = x1 + gap / 2 - width / 2
        } else {
          this.tspanX = x1 + space + arrowWidth
        }
      } else if (position === 'left' || position === 'right') {
        const textWidth = width + 2 * space + arrowWidth
        if (gap > textWidth) {
          this.tspanX = x1 + gap / 2 - width / 2
        } else {
          this.offset = textWidth - gap
          if (position === 'left') {
            this.tspanX = rowEnd - space - arrowWidth - width
          } else {
            this.tspanX = x1 + space + arrowWidth
          }
        }
      } else if (position === 'complete') {
        const textWidth = width + 2 * space
        if (gap > textWidth) {
          this.tspanX = x1 + gap / 2 - width / 2
        } else {
          const leftGap = x1 - rowStart
          const rightGap = rowEnd - x2
          const halfGap = (textWidth - gap) / 2
          if (leftGap < halfGap) {
            this.offset = leftGap
            this.offsetRight = 2 * halfGap - leftGap
            this.tspanX = rowStart + space
          } else if (rightGap < halfGap) {
            this.offsetRight = rightGap
            this.offset = 2 * halfGap - rightGap + 12
            this.tspanX = rowEnd - space - width
          } else {
            this.offset = halfGap
            this.offsetRight = halfGap
            this.tspanX = x1 - halfGap + space
          }
        }
      }
      this.breakX1 = this.tspanX - space
      this.breakX2 = this.tspanX + width + space
      this.initPosition = true
    },
    handleDblclick () {
      if (this.disabled) return

      const { id, type, fromEntity, toEntity } = this.relation
      this.handleEditRelation({
        id: id,
        type: type,
        entities: [
          {
            type: fromEntity[1],
            text: this.entityTypesMap[fromEntity[1]].name,
            id: fromEntity[0],
            entityText: fromEntity[3] 
          },
          {
            type: toEntity[1],
            text: this.entityTypesMap[toEntity[1]].name,
            id: toEntity[0],
            entityText: toEntity[3] 
          }
        ]
      })
    },
    handleMouseenter () {
      document.querySelectorAll(this.nodeClass).forEach(i => {
        i.classList.add('focus')
      })
    },
    handleMouseleave () {
      document.querySelectorAll(this.nodeClass).forEach(i => {
        i.classList.remove('focus')
      })
    }
  },

  mounted () {
    this.getPosition()
  }
}
</script>

<style>
</style>